/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.approvalformat.core.util;

import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationCollection;
import com.inspur.edp.cef.designtime.api.collection.GspEnumValueCollection;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.das.commonmodel.entity.GspCommonElement;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.element.UdtElement;
import com.inspur.edp.web.common.utility.StringUtility;
import lombok.Data;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Xu‘fa Wang
 * @date 2020/6/8 16:52
 */
@Data
public class TypeBuildingContext {
    private GspElementObjectType objectType = GspElementObjectType.None;
    private GspEnumValueCollection enums;
    private GspAssociationCollection associations;
    private String unifiedDataType;
    private GspElementDataType dataType;
    private Map<String, Object> params = new HashMap<>();
    private Map<String, Integer> typeNameMaps = new HashMap<>();
    private Map<String, Integer> elementIdMaps = new HashMap<>();
    private TypeBuildingContext root;
    private TypeBuildingContext parent;

    public boolean hasUnifiedDataType() {
        return this.getUnifiedDataType() != null && !StringUtils.isEmpty(this.getUnifiedDataType().trim());
    }

    public boolean inUnifiedDataType() {
        return this.getParent() != null && (this.getParent().hasUnifiedDataType() || this.getParent().inUnifiedDataType());
    }

    public boolean hasAssociation() {
        return this.getObjectType() == GspElementObjectType.Association && this.getAssociations() != null && this.getAssociations().size() > 0;
    }

    public boolean isDynamicField() {
        return this.getObjectType() == GspElementObjectType.DynamicProp;
    }

    public boolean isMultiLanguageField() {
        return (boolean) this.getParams().get("multiLanguageInput");
    }

    public boolean isMixinUDTAssociation() {
        return this.hasAssociation() && this.hasUnifiedDataType();
    }

    public String reviseElementId(String elementId) {
        String revisedElementId = elementId;
        if (this.parent != null && this.inUnifiedDataType()) {
            String parentId = (String) this.parent.getParams().get("id");
            revisedElementId = parentId.substring(0, 8) + elementId.substring(8);
        }
        Map<String, Integer> elementIdMaps = this.getRoot() != null ? this.getRoot().getElementIdMaps() : this.getElementIdMaps();
        if (elementIdMaps.containsKey(revisedElementId)) {
            elementIdMaps.put(revisedElementId, elementIdMaps.get(revisedElementId) + 1);
            revisedElementId = revisedElementId.substring(0, revisedElementId.length() - 2) + elementIdMaps.get(revisedElementId);
        } else {
            elementIdMaps.put(revisedElementId, 0);
        }
        return revisedElementId;
    }

    public String reviseTypeName(String suffixFieldId, String typeName) {
        String suffix = suffixFieldId.length() > 3 ? suffixFieldId.substring(0, 4) : suffixFieldId;
        String revisedTypeName = suffix.length() > 0 ? typeName + suffix.replace(suffix.substring(0, 1), suffix.substring(0, 1).toUpperCase()) : typeName;
        Map<String, Integer> typeNameMaps = this.getRoot() != null ? this.getRoot().getTypeNameMaps() : this.getTypeNameMaps();
        if (typeNameMaps.containsKey(revisedTypeName)) {
            typeNameMaps.put(revisedTypeName, typeNameMaps.get(revisedTypeName) + 1);
            return revisedTypeName + typeNameMaps.get(revisedTypeName);
        } else {
            typeNameMaps.put(revisedTypeName, 0);
            return revisedTypeName;
        }
    }

    public static TypeBuildingContext createTypeBuildingContext(IGspCommonField element, TypeBuildingContext parentContext) {
        if (element instanceof GspCommonElement) {
            return create((GspCommonElement) element, parentContext);
        } else if (element instanceof UdtElement) {
            return create((UdtElement) element, parentContext);
        }

        return null;
    }

    public static TypeBuildingContext create(GspCommonElement element, TypeBuildingContext parentContext) {
        if (element.getIsUdt()) {
            if (StringUtils.isEmpty(element.getUdtID()) || StringUtils.isEmpty(element.getUdtID().trim())) {
                throw new RuntimeException("标识为" + element.getId() + "，标签为" + element.getLabelID() + "的字段" + element.getName() + "被定义为'UnifiedDataType'字段，但是没有指定'UnifiedDataType'标识，请检查业务实体。");
            }
        }
        TypeBuildingContext context = new TypeBuildingContext() {
            {
                this.setObjectType(element.getObjectType());
                this.setEnums(element.getContainEnumValues());
                this.setAssociations(element.getChildAssociations());
                this.setDataType(element.getMDataType());
                this.setUnifiedDataType(element.getUdtID());
                this.setParams(new HashMap<String, Object>() {
                    {
                        this.put("id", element.getId() == null ? "" : element.getId());
                        this.put("code", element.getCode() == null ? "" : element.getCode());
                        this.put("name", element.getName() == null ? "" : element.getName());
                        this.put("label", element.getLabelID() == null ? "" : element.getLabelID());
                        this.put("bindingField", element.getLabelID() == null ? "" : element.getLabelID());
                        this.put("path", element.getLabelID() == null ? "" : element.getLabelID());
                        this.put("require", element.isRequired());
                        this.put("readonly", element.getReadonly());
                        this.put("defaultValue", element.getDefaultValue() == null ? "" : element.getDefaultValue());
                        this.put("length", element.getLength());
                        this.put("precision", element.getPrecision());
                        this.put("multiLanguageInput", false);
                    }
                });
                this.setRoot(parentContext != null && parentContext.getRoot() != null ? parentContext.getRoot() : parentContext);
                this.setParent(parentContext);
            }
        };
        return context;
    }

    public static TypeBuildingContext create(SimpleDataTypeDef udtTypeDef, TypeBuildingContext parentContext) {
        TypeBuildingContext context = new TypeBuildingContext() {
            {
                this.setObjectType(udtTypeDef.getObjectType());
                this.setEnums(udtTypeDef.getContainEnumValues());
                this.setAssociations(udtTypeDef.getChildAssociations());
                this.setDataType(udtTypeDef.getMDataType());
                this.setParams(new HashMap<String, Object>() {
                    {
                        this.put("id", udtTypeDef.getId() == null ? "" : udtTypeDef.getId());
                        this.put("code", udtTypeDef.getCode() == null ? "" : udtTypeDef.getCode());
                        this.put("name", udtTypeDef.getName() == null ? "" : udtTypeDef.getName());
                        this.put("label", udtTypeDef.getCode() == null ? "" : udtTypeDef.getCode());
                        this.put("bindingField", udtTypeDef.getCode() == null ? "" : udtTypeDef.getCode());
                        this.put("path", udtTypeDef.getCode() == null ? "" : udtTypeDef.getCode());
                        this.put("require", udtTypeDef.getIsRequired());
                        this.put("readonly", false);
                        this.put("defaultValue", udtTypeDef.getDefaultValue() == null ? "" : udtTypeDef.getDefaultValue());
                        this.put("length", udtTypeDef.getLength());
                        this.put("precision", udtTypeDef.getPrecision());
                        this.put("multiLanguageInput", false);
                    }
                });
                this.setRoot(parentContext != null && parentContext.getRoot() != null ? parentContext.getRoot() : parentContext);
                this.setParent(parentContext);
            }
        };

        updateTypeBuildingContext(context, parentContext);

        return context;
    }

    public static TypeBuildingContext create(UdtElement udtTypeDef, TypeBuildingContext parentContext) {
        if (udtTypeDef.getIsUdt()) {
            if (StringUtility.isNullOrEmpty(udtTypeDef.getUdtID()) || StringUtility.isNullOrEmpty(udtTypeDef.getUdtID().trim())) {
                throw new RuntimeException("标识为" + udtTypeDef.getId() + "，标签为" + udtTypeDef.getLabelID() + "的字段" + udtTypeDef.getName() + "被定义为'UnifiedDataType'字段，但是没有指定'UnifiedDataType'标识，请检查业务实体。");
            }
        }
        TypeBuildingContext typeBuildingContext = new TypeBuildingContext() {
            {
                this.setObjectType(udtTypeDef.getObjectType());
                this.setEnums(udtTypeDef.getContainEnumValues());
                this.setAssociations(udtTypeDef.getChildAssociations());
                this.setDataType(udtTypeDef.getMDataType());
                this.setUnifiedDataType(udtTypeDef.getUdtID());
                this.setParams(new HashMap<String, Object>(16) {
                    {
                        this.put("id", udtTypeDef.getId() == null ? "" : udtTypeDef.getId());
                        this.put("code", udtTypeDef.getCode() == null ? "" : udtTypeDef.getCode());
                        this.put("name", udtTypeDef.getName() == null ? "" : udtTypeDef.getName());
                        this.put("label", udtTypeDef.getLabelID() == null ? "" : udtTypeDef.getLabelID());
                        this.put("bindingField", udtTypeDef.getLabelID() == null ? "" : udtTypeDef.getLabelID());
                        this.put("path", udtTypeDef.getLabelID() == null ? "" : udtTypeDef.getLabelID());
                        this.put("require", udtTypeDef.getIsRequire());
                        this.put("readonly", false);
                        this.put("defaultValue", udtTypeDef.getDefaultValue() == null ? "" : udtTypeDef.getDefaultValue());
                        this.put("length", udtTypeDef.getLength());
                        this.put("precision", udtTypeDef.getPrecision());
                        this.put("multiLanguageInput", false);
                    }
                });
                this.setRoot(parentContext != null && parentContext.getRoot() != null ? parentContext.getRoot() : parentContext);
                this.setParent(parentContext);
            }
        };

        return typeBuildingContext;
    }

    private static void updateTypeBuildingContext(TypeBuildingContext typeBuildingContext, TypeBuildingContext parentContext) {
        if (parentContext == null || !parentContext.isMixinUDTAssociation()) {
            return;
        }

        if (parentContext.getAssociations() == null || parentContext.getAssociations().size() == 0) {
            return;
        }

        parentContext.getAssociations().forEach(association -> {
            if (association.getRefElementCollection() == null || association.getRefElementCollection().size() == 0) {
                return;
            }

            association.getRefElementCollection().forEach(refElement -> {
                if (refElement.getIsFromAssoUdt()) {
                    GspAssociationCollection associationCollection = typeBuildingContext.getAssociations();
                    if (associationCollection != null && associationCollection.size() > 0) {
                        if (!refElement.getIsFromAssoUdt()) {
                            associationCollection.get(0).getRefElementCollection().add(refElement);
                        }
                    }
                }
            });
        });
    }

    public static TypeBuildingContext createSimpleTypeContextFromAssociation(TypeBuildingContext context, TypeBuildingContext parent) {
        GspAssociationCollection associations = context.getAssociations();
        if (associations == null || associations.size() == 0) {
            throw new RuntimeException("字段" + context.getParams().get("name") + "不包含关联实体信息。");
        }
        Map<String, Object> params = context.getParams();
        return new TypeBuildingContext() {
            {
                this.setDataType(context.getDataType());
                this.setParams(new HashMap<String, Object>() {
                    {
                        this.put("id", associations.get(0).getId() == null ? "" : associations.get(0).getId());
                        this.put("code", params.get("code") == null ? "" : params.get("code"));
                        this.put("name", params.get("name") == null ? "" : params.get("name"));
                        this.put("label", params.get("label") == null ? "" : params.get("label"));
                        this.put("bindingField", params.get("label") == null ? "" : params.get("label"));
                        this.put("path", params.get("label") == null ? "" : params.get("label"));
                        this.put("require", params.get("require"));
                        this.put("readonly", params.get("readonly"));
                        this.put("defaultValue", params.get("defaultValue") == null ? "" : params.get("defaultValue"));
                        this.put("length", params.get("length"));
                        this.put("precision", params.get("precision"));
                        this.put("multiLanguageInput", false);
                    }
                });
                this.setRoot(parent != null && parent.getRoot() != null ? parent.getRoot() : parent);
                this.setParent(parent);
            }
        };
    }
}
